package com.inepex.inechart.shared.axes;

import java.util.ArrayList;
import java.util.TreeMap;

import com.inepex.inechart.client.Defaults;
import com.inepex.inechart.shared.CartesianAssist;
import com.inepex.inechart.shared.properties.Color;
import com.inepex.inechart.shared.properties.LineProperties;
import com.inepex.inegraphics.shared.Context;
import com.inepex.inegraphics.shared.DrawingArea;
import com.inepex.inegraphics.shared.DrawingAreaAssist;
import com.inepex.inegraphics.shared.GraphicalObjectContainer;
import com.inepex.inegraphics.shared.gobjects.Line;
import com.inepex.inegraphics.shared.gobjects.Rectangle;
import com.inepex.inegraphics.shared.gobjects.Text;
import com.inepex.inegraphics.shared.gobjects.Text.BasePointXPosition;
import com.inepex.inegraphics.shared.gobjects.Text.BasePointYPosition;

class AxisToGO {
	
	private static final int tickFillZIndex = Integer.MIN_VALUE;
	private static final int gridLineZIndex = tickFillZIndex + 1;	
	private static final int tickLineZIndex = gridLineZIndex + 1;
	private static final int axisLineZIndex = tickLineZIndex + 1;
	
	private TickFactory tickFactory;
	private TreeMap<Tick, Text> textMap;
	private DrawingArea drawingArea;
	
	
	public AxisToGO(TickFactory tickFactory, DrawingArea drawingArea) {
		super();
		this.tickFactory = tickFactory;
		this.drawingArea = drawingArea;
		textMap = new TreeMap<Tick, Text>();
	}

	protected Context createContext(LineProperties lineProperties) {
		return new Context(
				lineProperties.getLineColor().getAlpha(),
				lineProperties.getLineColor().getColor(),
				lineProperties.getLineWidth(),
				Defaults.colorString,
				0,
				0,
				Defaults.alpha,
				Defaults.colorString);
	}

	protected Context createFillContext(Color color) {
		return new Context(
				color.getAlpha(),
				color.getColor(),
				0,
				color.getColor(),
				0,
				0,
				Defaults.alpha,
				Defaults.colorString);
	}
	
	public void createTickLinesLabelsGrids(Axis axis, Axis perpAxis, GraphicalObjectContainer goc, GraphicalObjectContainer gridGOs, double startX, double startY){
		// ticks
		ArrayList<Tick> filtered = axis.getVisibleTicks();
		if (axis.isFilterFrequentTicks()){
			filtered = tickFactory.filterFequentTicks(axis, axis.getVisibleTicks());
		}
		CartesianAssist cartesianAssist =  axis.getModulToAlign().getCartesianAssist();
		for (Tick tick : filtered) {
			// calculate the position
			double tickStartX = 0, tickStartY = 0, tickEndX = 0, tickEndY = 0, gridStartX, gridStartY, gridEndX, gridEndY;
			
			if (axis.isHorizontal()) {
				gridEndX = gridStartX = tickStartX = tickEndX = cartesianAssist.modelToCanvasHorizontal(tick.position);
				gridStartY = axis.getModulToAlign().getTopPadding();
				gridEndY = cartesianAssist.getChartHeight() - axis.getModulToAlign().getBottomPadding();
				switch (tick.tickPosition) {
				case Cross:
					tickStartY = startY - tick.tickLength / 2;
					tickEndY = tickStartY + tick.tickLength;
					break;
				case To_Higher_Values:
					tickStartY = startY;
					if (perpAxis.axisDirection == AxisDirection.Vertical_Ascending_To_Bottom) {
						tickEndY = tickStartY - tick.tickLength;
					} else {
						tickEndY = tickStartY + tick.tickLength;
					}
					break;
				case To_Lower_Values:
					tickStartY = startY;
					if (perpAxis.axisDirection == AxisDirection.Vertical_Ascending_To_Bottom) {
						tickEndY = tickStartY + tick.tickLength;
					} else {
						tickEndY = tickStartY - tick.tickLength;
					}
					break;
				}
			} 
			else {
				gridEndY = gridStartY = tickStartY = tickEndY = cartesianAssist.modelToCanvasVertical(tick.position);
				gridStartX = axis.getModulToAlign().getLeftPadding();
				gridEndX = cartesianAssist.getChartWidth() - axis.getModulToAlign().getRightPadding();
				switch (tick.tickPosition) {
				case Cross:
					tickStartX = startX - tick.tickLength / 2;
					tickEndX = tickStartX + tick.tickLength;
					break;
				case To_Lower_Values:
					tickStartX = startX;
					if (perpAxis.axisDirection == AxisDirection.Horizontal_Ascending_To_Left) {
						tickEndX = tickStartX + tick.tickLength;
					} else {
						tickEndX = tickStartX - tick.tickLength;
					}
					break;
				case To_Higher_Values:
					tickStartX = startX;
					if (perpAxis.axisDirection == AxisDirection.Horizontal_Ascending_To_Left) {
						tickEndX = tickStartX - tick.tickLength;
					} else {
						tickEndX = tickStartX + tick.tickLength;
					}
					break;
				}
			}
			// Tick line
			if (tick.tickLine != null && tick.tickLength > 0) {
				Line tickLine = new Line(tickStartX, tickStartY, tickEndX, tickEndY, createContext(tick.tickLine), tickLineZIndex);
				goc.add(tickLine);
			}
			// Grid line
			if (gridGOs != null && tick.gridLine != null) {
				Line gridLine = new Line(gridStartX, gridStartY, gridEndX, gridEndY, createContext(tick.gridLine), gridLineZIndex);
				if(tick.gridLine.getDashDistance() > 0){
					gridGOs.add(DrawingAreaAssist.createDashedLine(gridLine, tick.gridLine.getDashStrokeLength(), tick.gridLine.getDashDistance()));
				}
				else{
					gridGOs.add(gridLine);
				}
			}
			createTickText(axis, perpAxis, tick, tickStartX, tickStartY);
		}	
	}
	
	public void createTickText(Axis axis, Axis perpAxis, Tick tick, double tickStartX, double tickStartY){
		if(tick.formatString != null && (axis.tickCustomizer == null || tick.text == null || tick.text.getText() == null)){
			tick.text.setText( tickFactory.formatTickText(tick, axis.axisDataType));
		}
		if (tick.text != null && tick.text.getText().length() > 0) {
			//calculate the text position relative to tick
			BasePointXPosition h = null;
			BasePointYPosition v = null;
			switch (tick.textContainer.getHorizontalPosition()) {
			case Auto:
				if (axis.isHorizontal()) {
					h = BasePointXPosition.MIDDLE;
				} 
				else {
					if (axis.getAxisPosition() == AxisPosition.Maximum && perpAxis.getAxisDirection() == AxisDirection.Horizontal_Ascending_To_Left ||
							axis.getAxisPosition() == AxisPosition.Minimum && perpAxis.getAxisDirection() == AxisDirection.Horizontal_Ascending_To_Right) {
						h = BasePointXPosition.RIGHT;
						tickStartX -= 4;
					}
					else {
						h = BasePointXPosition.LEFT;
						tickStartX += tick.tickLength + 4;
					}
				}
				break;
			case Left:
				h = BasePointXPosition.LEFT;
				break;
			case Right:
				h = BasePointXPosition.RIGHT;
				break;
			case Middle:
				h = BasePointXPosition.MIDDLE;
				break;
			}
			switch (tick.textContainer.getVerticalPosition()) {
			case Auto:
				if (!axis.isHorizontal()) {
					v = BasePointYPosition.MIDDLE;
				}
				else {
					if (axis.getAxisPosition() == AxisPosition.Maximum && perpAxis.getAxisDirection() == AxisDirection.Vertical_Ascending_To_Bottom ||
							axis.getAxisPosition() == AxisPosition.Minimum && perpAxis.getAxisDirection() == AxisDirection.Vertical_Ascending_To_Top) {
						v = BasePointYPosition.TOP;
						tickStartY += tick.tickLength + 2;
					} 
					else {
						v = BasePointYPosition.BOTTOM;
						tickStartY += 1;
					}
				}
				break;
			case Bottom:
				v = BasePointYPosition.BOTTOM;
				break;
			case Middle:
				v = BasePointYPosition.MIDDLE;
				break;
			case Top:
				v = BasePointYPosition.TOP;
				break;
			}
			Text text = new Text(tick.text.getText(), tickStartX, tickStartY, h, v);
			text.setFontFamily(tick.text.getTextProperties().getFontFamily());
			text.setFontStyle(tick.text.getTextProperties().getFontStyle());
			text.setFontWeight(tick.text.getTextProperties().getFontWeight());
			text.setColor(tick.text.getTextProperties().getColor().getColor());
			text.setFontSize(tick.text.getTextProperties().getFontSize());
			drawingArea.measureText(text);
			text.setWidth( (int) (text.getWidth() +
					tick.textContainer.getLeftPadding() + tick.textContainer.getRightPadding() +
					tick.textContainer.getBackground().getLineProperties().getLineWidth() * 2) + 1);
			text.setHeight( (int) (text.getHeight() + 
					tick.textContainer.getTopPadding() + tick.textContainer.getBottomPadding() +
					tick.textContainer.getBackground().getLineProperties().getLineWidth() * 2) + 1);
			textMap.put(tick, text);			
		}
	}
	
	public void createAxisLine(Axis axis, GraphicalObjectContainer goc, double startX, double startY, double endX, double endY){
		// TODO upper-, lower End
		if (axis.getLineProperties() != null) {
			Line axisLine = new Line(startX, startY, endX, endY, createContext(axis.lineProperties), axisLineZIndex);
			goc.add(axisLine);
		}
	}

	public void createFillBetweenTicks(Axis axis, GraphicalObjectContainer goc){
		double x, y, width, height;
		CartesianAssist cartesianAssist =  axis.getModulToAlign().getCartesianAssist();
		for (Object[] tickPair : axis.getGridFills()) {
			if(((Tick) tickPair[0]).position >= axis.max || ((Tick) tickPair[1]).position <= axis.min)
				continue;
			double tick1,tick2;
			if (axis.isHorizontal()) {
				tick1 = Math.max(cartesianAssist.modelToCanvasHorizontal(((Tick) tickPair[0]).position), axis.modulToAlign.getLeftPadding());
				tick2 = Math.min(cartesianAssist.modelToCanvasHorizontal(((Tick) tickPair[1]).position), axis.modulToAlign.getCartesianAssist().getRightEnd());
//				x = Math.max(axis.modulToAlign.getLeftPadding(), Math.min(tick1, tick2));
				x = tick1;
				y = axis.getModulToAlign().getTopPadding();
				height = axis.getModulToAlign().getCartesianAssist().getHeight();
				width = tick2 - tick1;
//				width = Math.min(Math.abs(tick2 - tick1), canvas.getWidth() - axis.modulToAlign.getRightPadding() - x);
			}
			else { //TODO
				tick1 = cartesianAssist.modelToCanvasVertical(((Tick) tickPair[0]).position);
				tick2 = cartesianAssist.modelToCanvasVertical(((Tick) tickPair[1]).position);
				x = axis.modulToAlign.getLeftPadding();
				y = Math.max(axis.modulToAlign.getTopPadding(), Math.min(tick1, tick2));
				width = axis.modulToAlign.getCartesianAssist().getWidth();
				height = Math.min(Math.abs(tick1 - tick2), cartesianAssist.getChartHeight() - axis.modulToAlign.getBottomPadding() - y);
			}

			Rectangle fill = new Rectangle(
					x,
					y,
					width,
					height,
					0,
					false,
					true,
					createFillContext((Color) tickPair[2]),
					tickFillZIndex);
			goc.add(fill);
		}
	}


}
